<template>
  <div>
    <el-upload
      v-show="false"
      ref="uploadRef"
      accept=".txt, .json, .proto"
      :limit="1"
      :http-request="handleUpload"
      :before-upload="beforeAvatarUpload"
      :auto-upload="true"
    >
    </el-upload>
    <el-row class="mb-4">
      <div ref="containerRef" className="app-content" />
      <div ref="miniContainerRef"></div>
    </el-row>
    <elm-er-dialog
      v-model:show="erDesignDialog"
      v-model:content="erInfo"
      @commit="commitErDialog"
    />
    <elm-eredge-dialog
      v-model:show="edgeEditDialog"
      v-model:connect="connect"
      :show-delete="!isNewEdage"
      @delete="deleteEdge"
      @cancel="cancelEdgeDialog"
      @commit="commitEdgeDialog"
    />
    <elm-ertable-dialog
      v-model:show="tableEditDialog"
      v-model:data="tableInfo"
      :show-delete="!!nodeEdit"
      :tables="antv6.JsonStrToErDesign(erInfo).tables"
      @delete="deleteTable"
      @cancel="cancelTableDialog"
      @commit="commitTableDialog"
    />
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref, onMounted, onActivated, onDeactivated, Ref } from "vue";
import {
  Graph,
  Node,
  Edge,
  Point,
  ErDesign,
  ErTable,
} from "@/components/x6antv/index";
import * as antv6 from "@/components/x6antv/index";
import { mkGraph } from "./Graph";
import { SampleData } from "./SampleData";
import {
  showContextMenu,
  ContextMenuItemEvent,
  load_sample,
  open_er_code,
  open_loacl_file,
} from "./ContextMenu";
import { ElMessageBox, UploadProps } from "element-plus";
import { Callback } from "@/lib/vars";
import type { UploadInstance } from "element-plus";
import { GraphNodeApi } from "@/apis/home";

// 界面参数
const containerRef: Ref<HTMLDivElement | undefined> = ref(),
  miniContainerRef: Ref<HTMLDivElement | undefined> = ref(),
  graphScrollbarPosition: any = ref(),
  firstActivated = ref(true),
  uploadRef = ref<UploadInstance>(),
  loadFile = ref();

// ER图内容参数
const erInfo = ref(""),
  erDesignDialog = ref(false);

// 表编辑框参数
const tableEditDialog = ref(false),
  tableInfo = ref<ErTable>();

// 表关系线编辑参数
const edgeEditDialog = ref(false),
  isNewEdage = ref(false),
  connect = ref({
    source: {
      tbName: "",
      tbLabel: "",
      itemName: "",
      relation: 0,
    },
    target: {
      tbName: "",
      tbLabel: "",
      itemName: "",
      relation: 0,
    },
  });

// 常量参数
const tipTitle = "提示",
  confirm = "确认",
  warning = "warning";

// 临时变量参数
let graph: Graph,
  nodeEdit: Node | undefined | null,
  edgeEdit: Edge | undefined | null,
  lPoint: Point | null;

// ER图内容提交  TODO：可考虑是不是要用图内容来对比更新
const commitErDialog = () => {
  graph.clearCells();
  if (erInfo.value) {
    let erDesign: ErDesign = {
      id: "",
      name: "",
      tables: [],
      edges: [],
    };
    Object.assign(erDesign, JSON.parse(erInfo.value));
    antv6.dataToNodes(erDesign).forEach((node: any) => {
      graph.addNode(node);
    });
    antv6.dataToEdge(erDesign).forEach((edge: any) => {
      graph.addEdge(edge);
    });
  }
};

// 表关系线编辑控制
const reSetEdgeInfo = () => {
    edgeEdit = null;
    connect.value = {
      source: {
        tbName: "",
        tbLabel: "",
        itemName: "",
        relation: 0,
      },
      target: {
        tbName: "",
        tbLabel: "",
        itemName: "",
        relation: 0,
      },
    };
    isNewEdage.value = false;
  },
  deleteEdge = () => {
    if (edgeEdit) {
      edgeEdit.remove();
      erInfo.value = antv6.PullEdgeFromErDesign(erInfo.value, edgeEdit);
    }
    reSetEdgeInfo();
  },
  cancelEdgeDialog = () => {
    if (edgeEdit) {
      if (isNewEdage.value) graph.removeEdge(edgeEdit);
    }
    reSetEdgeInfo();
  },
  commitEdgeDialog = () => {
    if (edgeEdit) {
      if (connect.value.source.relation === 1)
        edgeEdit.attr(antv6.sMarkerPath, antv6.plus);
      else edgeEdit.attr(antv6.sMarkerPath, antv6.path);
      if (connect.value.target.relation === 1)
        edgeEdit.attr(antv6.tMarkerPath, antv6.plus);
      else edgeEdit.attr(antv6.tMarkerPath, antv6.path);
      if (isNewEdage.value)
        erInfo.value = antv6.PushEdgeToErDesign(erInfo.value, edgeEdit);
      else erInfo.value = antv6.UpdateEdgeToErDesign(erInfo.value, edgeEdit);
    }
    reSetEdgeInfo();
  };

const reSetTableInfo = () => {
    nodeEdit = null;
    lPoint = null;
    tableInfo.value = ref<ErTable | any>({}).value;
  },
  deleteTable = () => {
    if (nodeEdit && tableInfo.value) {
      nodeEdit.remove();
      erInfo.value = antv6.PullTableFromErDesign(erInfo.value, tableInfo.value);
    }
    reSetTableInfo();
  },
  cancelTableDialog = () => {
    reSetTableInfo();
  },
  commitTableDialog = () => {
    if (!tableInfo.value?.label) {
      ElMessageBox.alert("表格名称不能为空", tipTitle, {
        confirmButtonText: confirm,
        type: warning,
      });
      return;
    }
    if (!tableInfo.value?.items || tableInfo.value?.items.length === 0) {
      ElMessageBox.alert("表格数据字段不能为空", tipTitle, {
        confirmButtonText: confirm,
        type: warning,
      });
      return;
    }
    if (tableInfo.value) {
      let tInfo = tableInfo.value;
      if (!tInfo.id) {
        tInfo.id = antv6.GetMaxTableIdFromErDesign(erInfo.value) + 1 + "";
      }
      if (lPoint) {
        tInfo.x = lPoint.x;
        tInfo.y = lPoint.y;
      }
      if (!nodeEdit) {
        graph.addNode(antv6.dataToNode(tInfo));
        erInfo.value = antv6.PushTableToErDesign(erInfo.value, tInfo);
      } else {
        // 修改高度
        antv6.SetNodeHeightByData(tInfo, nodeEdit);
        // 修改表名
        const nodeLabel = antv6.NodeLabel(tInfo.name, tInfo.label);
        if (nodeEdit.attr("text/text") != nodeLabel)
          nodeEdit.attr("text/text", nodeLabel);
        // 修改字段
        antv6.UpdateNodePortsByErTableInfo(nodeEdit, tInfo);
        // 更新表数据
        erInfo.value = antv6.UpdateTableToErDesign(
          erInfo.value,
          tableInfo.value
        );
      }
    }
    reSetTableInfo();
  };

const showEditPaperView = () => {
    erDesignDialog.value = true;
  },
  tableNodeEdit = (node: Node) => {
    let er = antv6.JsonStrToErDesign(erInfo.value);
    let tbs = er.tables.filter((table) => {
      return table.id == node.id;
    });
    if (tbs.length > 0) {
      const tb = tbs[0];
      if (er.edges) {
        for (let j = 0; j < tb.items.length; j++) {
          const item = tb.items[j];
          item.cs = antv6.ItemCs(tb.id, item.id, er.edges);
        }
      }
      tableInfo.value = tb;
      tableEditDialog.value = true;
      nodeEdit = node;
      lPoint = null;
    }
  },
  newTableNodeEdit = (x: number, y: number, isLocal: boolean) => {
    if (!isLocal) lPoint = graph.pageToLocal(x, y);
    else lPoint = new Point(x, y);
    tableEditDialog.value = true;
  },
  connectEdgeEdit = (edge: Edge) => {
    console.log(edge.getSourcePortId(), edge.getTargetPortId());
    isNewEdage.value = false;
    const cn = antv6.GetNameFromEdge(erInfo.value, edge);
    if (antv6.plus === edge.attr(antv6.sMarkerPath)) cn.source.relation = 1;
    else cn.source.relation = 0;
    if (antv6.plus === edge.attr(antv6.tMarkerPath)) cn.target.relation = 1;
    else cn.target.relation = 0;
    connect.value = cn;
    edgeEditDialog.value = true;
    edgeEdit = edge;
  },
  newConnectEdgeEdit = (isNew: boolean, edge: Edge) => {
    isNewEdage.value = isNew;
    connect.value = antv6.GetNameFromEdge(erInfo.value, edge);
    edgeEditDialog.value = true;
    edgeEdit = edge;
  };

const ContextMenuCallback: Callback<ContextMenuItemEvent> = (
  error: Error | null,
  data: ContextMenuItemEvent
): void => {
  if (data.itemId === load_sample) {
    erInfo.value = JSON.stringify(SampleData, null, 2);
    erDesignDialog.value = true;
  }
  if (data.itemId === open_er_code) {
    showEditPaperView();
  }
  if (data.itemId === open_loacl_file) {
    uploadRef.value?.$el.querySelector("input").click();
  }
};

const beforeAvatarUpload: UploadProps["beforeUpload"] = (file) => {
    console.log(file);
    var reader = new FileReader();
    reader.onload = () => {
      if (reader.result) {
        erInfo.value = JSON.stringify(
          JSON.parse(reader.result.toString()),
          null,
          2
        );
        erDesignDialog.value = true;
      }
    };
    reader.readAsText(file);
    return false;
  },
  handleUpload: UploadProps["httpRequest"] = (file: any) => {
    return new Promise(() => {
      console.log("upload");
    });
  };

onActivated(() => {
  if (!firstActivated.value) {
    graph.setScrollbarPosition(
      graphScrollbarPosition.value.left,
      graphScrollbarPosition.value.top
    );
  } else {
    firstActivated.value = false;
  }
});
onDeactivated(() => {
  graphScrollbarPosition.value = graph.getScrollbarPosition();
});

onMounted(async () => {
  if (containerRef.value && miniContainerRef.value) {
    graph = mkGraph(containerRef.value, miniContainerRef.value);
    graph.on("view:mounted", () => {
      //console.log("haha");
    });
    graph.on("render:done", () => {
      //console.log("render:done");
    });
    graph.on("translate", ({ tx, ty }) => {
      console.log(tx, ty);
    });
    graph.on("node:mouseenter", ({ e }) => {
      e.stopPropagation();
    });

    graph.on("edge:connected", ({ isNew, edge }) => {
      newConnectEdgeEdit(isNew, edge);
    });
    graph.on("edge:dblclick", ({ e, x, y, edge, view }: any) => {
      connectEdgeEdit(edge);
    });
    graph.on("node:dblclick", ({ e, x, y, node, view }: any) => {
      tableNodeEdit(node);
    });
    graph.on("blank:dblclick", ({ e, x, y }) => {
      newTableNodeEdit(x, y, true);
    });

    graph.on("blank:contextmenu", ({ e, x, y }) => {
      const p = graph.localToPage(x, y);
      showContextMenu(p.x, p.y, ContextMenuCallback);
    });
    graph.on("node:moved", ({ e, x, y, node, view }) => {
      erInfo.value = antv6.UpdateErTablePosition(
        node.id,
        node.getPosition().x,
        node.getPosition().y,
        erInfo.value
      );
    });
    graph.on("node:resized", ({ node }) => {
      erInfo.value = antv6.UpdateErTableWidth(
        node.id,
        node.size().width,
        erInfo.value
      );
    });
  }
});
</script>

<style lang="scss" scoped>
.app-content {
  width: 75vw;
  height: 70vh;
  margin-top: 16px;
  margin-right: 8px;
  margin-left: 8px;
  border-radius: 5px;
  box-shadow: 0 12px 5px -10px rgb(0 0 0 / 10%), 0 0 4px 0 rgb(0 0 0 / 10%);
}
</style>
